package com.education.ai.service.impl;

import com.education.ai.entity.Teacher;
import com.education.ai.entity.Student;
import com.education.ai.entity.Admin;
import com.education.ai.exception.BusinessException;
import com.education.ai.model.dto.LoginRequest;
import com.education.ai.model.dto.ChangePasswordRequest;
import com.education.ai.model.dto.RegisterRequest;
import com.education.ai.model.dto.TeacherResponse;
import com.education.ai.model.dto.StudentResponse;
import com.education.ai.model.dto.AdminResponse;
import com.education.ai.repository.TeacherRepository;
import com.education.ai.repository.StudentRepository;
import com.education.ai.repository.AdminRepository;
import com.education.ai.service.AuthService;
import com.education.ai.util.PasswordUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 认证服务实现类
 */
@Service
public class AuthServiceImpl implements AuthService {

    private static final Logger logger = LoggerFactory.getLogger(AuthServiceImpl.class);

    // JWT密钥
    @Value("${jwt.secret:defaultSecret}")
    private String jwtSecret;
    
    // JWT过期时间（12小时）
    @Value("${jwt.expiration:43200000}")
    private long jwtExpiration;
    
    @Autowired
    private TeacherRepository teacherRepository;
    
    @Autowired
    private StudentRepository studentRepository;
    
    @Autowired
    private AdminRepository adminRepository;
    
    // 存储已登录的会话
    private static final Map<String, String> activeTokens = new ConcurrentHashMap<>();
    
    @Override
    @Transactional
    public Object register(RegisterRequest registerRequest) {
        try {
            logger.info("开始用户注册流程 - 角色: {}, 姓名: {}, 手机: {}", 
                registerRequest.getRole(), registerRequest.getName(), registerRequest.getPhone());
            logger.info("接收到的密码: {}", registerRequest.getPassword());
            logger.info("接收到的确认密码: {}", registerRequest.getConfirmPassword());
            // 验证两次密码是否一致
            if (!registerRequest.getPassword().equals(registerRequest.getConfirmPassword())) {
                logger.warn("注册失败 - 两次密码不一致 - 姓名: {}", registerRequest.getName());
                throw new BusinessException("两次输入的密码不一致");
            }
            
            // 根据角色进行不同的注册逻辑
            switch (registerRequest.getRole()) {
                case "teacher":
                    return registerTeacher(registerRequest);
                case "student":
                    return registerStudent(registerRequest);
                case "admin":
                    return registerAdmin(registerRequest);
                default:
                    throw new BusinessException("无效的用户角色");
            }
        } catch (BusinessException be) {
            throw be;
        } catch (Exception e) {
            logger.error("注册失败 - 系统错误", e);
            throw new BusinessException("注册失败，请稍后重试");
        }
    }
    
    private TeacherResponse registerTeacher(RegisterRequest request) {
        // 验证工号是否已存在
        if (teacherRepository.existsByTeacherId(request.getLoginId())) {
            throw new BusinessException("该工号已被注册");
        }
        
        // 验证手机号是否已存在
        if (teacherRepository.existsByPhone(request.getPhone())) {
            throw new BusinessException("该手机号已被注册");
        }
        
        // 生成盐值和加密密码
        String salt = PasswordUtil.generateSalt();
        String encryptedPassword = PasswordUtil.encryptPassword(request.getPassword(), salt);
        
        // 创建教师对象
        Teacher teacher = Teacher.builder()
                .teacherId(request.getLoginId())
                .name(request.getName())
                .password(encryptedPassword)
                .salt(salt)
                .phone(request.getPhone())
                .email(request.getEmail())
                .status(1)
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
        
        teacher = teacherRepository.save(teacher);
        
        return convertToTeacherResponse(teacher);
    }
    
    private StudentResponse registerStudent(RegisterRequest request) {
        logger.info("开始注册学生 - 学号: {}, 姓名: {}, 班级: {}", 
            request.getLoginId(), request.getName(), request.getClassName());
        
        // 验证学号是否已存在
        if (studentRepository.existsByStudentId(request.getLoginId())) {
            logger.warn("注册失败 - 学号已存在: {}", request.getLoginId());
            throw new BusinessException("该学号已被注册");
        }
        
        // 验证手机号是否已存在
        if (studentRepository.existsByPhone(request.getPhone())) {
            logger.warn("注册失败 - 手机号已存在: {}", request.getPhone());
            throw new BusinessException("该手机号已被注册");
        }

        // 验证班级是否为空
        if (request.getClassName() == null || request.getClassName().trim().isEmpty()) {
            logger.warn("注册失败 - 班级不能为空");
            throw new BusinessException("班级不能为空");
        }
        
        // 生成盐值和加密密码
        String salt = PasswordUtil.generateSalt();
        String encryptedPassword = PasswordUtil.encryptPassword(request.getPassword(), salt);
        logger.info("密码加密完成 - 盐值: {}", salt);
        
        // 创建学生对象
        Student student = Student.builder()
                .studentId(request.getLoginId())
                .name(request.getName())
                .password(encryptedPassword)
                .salt(salt)
                .phone(request.getPhone())
                .email(request.getEmail())
                .className(request.getClassName())
                .status(1)
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
        
        logger.info("准备保存学生信息到数据库");
        try {
            student = studentRepository.save(student);
            logger.info("学生信息保存成功 - ID: {}", student.getId());
        } catch (Exception e) {
            logger.error("保存学生信息失败", e);
            throw new BusinessException("注册失败，请稍后重试");
        }
        
        return convertToStudentResponse(student);
    }
    
    private AdminResponse registerAdmin(RegisterRequest request) {
        // 验证账号是否已存在
        if (adminRepository.existsByAdminId(request.getLoginId())) {
            throw new BusinessException("该账号已被注册");
        }
        
        // 验证手机号是否已存在
        if (adminRepository.existsByPhone(request.getPhone())) {
            throw new BusinessException("该手机号已被注册");
        }
        
        // 创建管理员对象，直接使用原始密码
        Admin admin = Admin.builder()
                .adminId(request.getLoginId())
                .name(request.getName())
                .password(request.getPassword())  // 直接使用原始密码
                .phone(request.getPhone())
                .email(request.getEmail())
                .status(1)
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
        
        admin = adminRepository.save(admin);
        
        return convertToAdminResponse(admin);
    }
    
    @Override
    public Object login(LoginRequest loginRequest) {
        try {
            logger.info("开始用户登录流程 - 角色: {}, 登录方式: {}", 
                loginRequest.getRole(), loginRequest.getLoginType());
            
            // 根据角色进行不同的登录逻辑
            switch (loginRequest.getRole()) {
                case "teacher":
                    return loginTeacher(loginRequest);
                case "student":
                    return loginStudent(loginRequest);
                case "admin":
                    return loginAdmin(loginRequest);
                default:
                    throw new BusinessException("无效的用户角色");
            }
        } catch (BusinessException be) {
            throw be;
        } catch (Exception e) {
            logger.error("登录失败 - 系统错误", e);
            throw new BusinessException("登录失败，请稍后重试");
        }
    }
    
    private TeacherResponse loginTeacher(LoginRequest request) {
        Teacher teacher;
        
        // 根据登录方式查找教师
        if (request.getLoginType() == 1) {
            teacher = teacherRepository.findByTeacherId(request.getLoginId())
                    .orElseThrow(() -> new BusinessException("工号不存在"));
        } else {
            teacher = teacherRepository.findByPhone(request.getLoginId())
                    .orElseThrow(() -> new BusinessException("手机号不存在"));
        }
        
        // 验证密码
        String encryptedPassword = PasswordUtil.encryptPassword(request.getPassword(), teacher.getSalt());
        if (!encryptedPassword.equals(teacher.getPassword())) {
            throw new BusinessException("密码错误");
        }
        
        // 验证账号状态
        if (teacher.getStatus() != 1) {
            throw new BusinessException("账号已被禁用");
        }
        
        return convertToTeacherResponse(teacher);
    }
    
    private StudentResponse loginStudent(LoginRequest request) {
        Student student;
        
        // 根据登录方式查找学生
        if (request.getLoginType() == 1) {
            student = studentRepository.findByStudentId(request.getLoginId())
                    .orElseThrow(() -> new BusinessException("学号不存在"));
        } else {
            student = studentRepository.findByPhone(request.getLoginId())
                    .orElseThrow(() -> new BusinessException("手机号不存在"));
        }
        
        // 验证密码
        String encryptedPassword;
        if (student.getSalt() != null && !student.getSalt().isEmpty()) {
            encryptedPassword = PasswordUtil.encryptPassword(request.getPassword(), student.getSalt());
        } else {
            // 如果没有盐值，直接使用密码
            encryptedPassword = request.getPassword();
        }
        
        if (!encryptedPassword.equals(student.getPassword())) {
            throw new BusinessException("密码错误");
        }
        
        // 验证账号状态
        if (student.getStatus() != 1) {
            throw new BusinessException("账号已被禁用");
        }
        
        return convertToStudentResponse(student);
    }
    
    private AdminResponse loginAdmin(LoginRequest request) {
        Admin admin;
        
        // 根据登录方式查找管理员
        if (request.getLoginType() == 1) {
            admin = adminRepository.findByAdminId(request.getLoginId())
                    .orElseThrow(() -> new BusinessException("账号不存在"));
        } else {
            admin = adminRepository.findByPhone(request.getLoginId())
                    .orElseThrow(() -> new BusinessException("手机号不存在"));
        }
        
        // 直接比较密码，不进行加密
        if (!request.getPassword().equals(admin.getPassword())) {
            throw new BusinessException("密码错误");
        }
        
        // 验证账号状态
        if (admin.getStatus() != 1) {
            throw new BusinessException("账号已被禁用");
        }
        
        return convertToAdminResponse(admin);
    }
    
    @Override
    public TeacherResponse findTeacherByName(String name) {
        Teacher teacher = teacherRepository.findByName(name)
                .orElseThrow(() -> new BusinessException("教师不存在"));
        return convertToTeacherResponse(teacher);
    }
    
    @Override
    public StudentResponse findStudentByName(String name) {
        Student student = studentRepository.findByName(name)
                .orElseThrow(() -> new BusinessException("学生不存在"));
        return convertToStudentResponse(student);
    }
    
    @Override
    public AdminResponse findAdminByName(String name) {
        Admin admin = adminRepository.findByName(name)
                .orElseThrow(() -> new BusinessException("管理员不存在"));
        return convertToAdminResponse(admin);
    }
    
    @Override
    public void changePassword(String userId, String role, ChangePasswordRequest request) {
        // 验证两次密码是否一致
        if (!request.getNewPassword().equals(request.getConfirmPassword())) {
            throw new BusinessException("两次输入的密码不一致");
        }
        
        switch (role) {
            case "teacher":
                changeTeacherPassword(userId, request);
                break;
            case "student":
                changeStudentPassword(userId, request);
                break;
            case "admin":
                changeAdminPassword(userId, request);
                break;
            default:
                throw new BusinessException("无效的用户角色");
        }
    }
    
    private void changeTeacherPassword(String teacherId, ChangePasswordRequest request) {
        Teacher teacher = teacherRepository.findByTeacherId(teacherId)
                .orElseThrow(() -> new BusinessException("教师不存在"));
        
        // 验证旧密码
        String encryptedOldPassword = PasswordUtil.encryptPassword(request.getOldPassword(), teacher.getSalt());
        if (!encryptedOldPassword.equals(teacher.getPassword())) {
            throw new BusinessException("旧密码错误");
        }
        
        // 生成新密码
        String salt = PasswordUtil.generateSalt();
        String encryptedNewPassword = PasswordUtil.encryptPassword(request.getNewPassword(), salt);
        
        teacher.setPassword(encryptedNewPassword);
        teacher.setSalt(salt);
        teacher.setUpdateTime(LocalDateTime.now());
        
        teacherRepository.save(teacher);
    }
    
    private void changeStudentPassword(String studentId, ChangePasswordRequest request) {
        Student student = studentRepository.findByStudentId(studentId)
                .orElseThrow(() -> new BusinessException("学生不存在"));
        
        // 验证旧密码
        String encryptedOldPassword = PasswordUtil.encryptPassword(request.getOldPassword(), student.getSalt());
        if (!encryptedOldPassword.equals(student.getPassword())) {
            throw new BusinessException("旧密码错误");
        }
        
        // 生成新密码
        String salt = PasswordUtil.generateSalt();
        String encryptedNewPassword = PasswordUtil.encryptPassword(request.getNewPassword(), salt);
        
        student.setPassword(encryptedNewPassword);
        student.setSalt(salt);
        student.setUpdateTime(LocalDateTime.now());
        
        studentRepository.save(student);
    }
    
    private void changeAdminPassword(String adminId, ChangePasswordRequest request) {
        Admin admin = adminRepository.findByAdminId(adminId)
                .orElseThrow(() -> new BusinessException("管理员不存在"));
        
        // 验证旧密码
        if (!request.getOldPassword().equals(admin.getPassword())) {
            throw new BusinessException("旧密码错误");
        }
        
        // 直接设置新密码，不进行加密
        admin.setPassword(request.getNewPassword());
        admin.setUpdateTime(LocalDateTime.now());
        
        adminRepository.save(admin);
    }
    
    @Override
    public Object getUserById(String userId, String role) {
        switch (role) {
            case "teacher":
                return getTeacherById(userId);
            case "student":
                return getStudentById(userId);
            case "admin":
                return getAdminById(userId);
            default:
                throw new BusinessException("无效的用户角色");
        }
    }
    
    @Override
    public TeacherResponse getTeacherById(String teacherId) {
        Teacher teacher = teacherRepository.findByTeacherId(teacherId)
                .orElseThrow(() -> new BusinessException("教师不存在"));
        return convertToTeacherResponse(teacher);
    }
    
    private StudentResponse getStudentById(String studentId) {
        Student student = studentRepository.findByStudentId(studentId)
                .orElseThrow(() -> new BusinessException("学生不存在"));
        return convertToStudentResponse(student);
    }
    
    private AdminResponse getAdminById(String adminId) {
        Admin admin = adminRepository.findByAdminId(adminId)
                .orElseThrow(() -> new BusinessException("管理员不存在"));
        return convertToAdminResponse(admin);
    }
    
    @Override
    public String generateToken(String userId, String role) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + jwtExpiration);
        
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("role", role);
        
        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, jwtSecret)
                .compact();
    }
    
    @Override
    public String getUserIdFromToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(jwtSecret)
                .parseClaimsJws(token)
                .getBody();
        
        return claims.get("userId", String.class);
    }
    
    @Override
    public String getRoleFromToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(jwtSecret)
                .parseClaimsJws(token)
                .getBody();
        
        return claims.get("role", String.class);
    }
    
    @Override
    public void logout(String userId, String role) {
        String key = userId + ":" + role;
        activeTokens.remove(key);
    }
    
    private TeacherResponse convertToTeacherResponse(Teacher teacher) {
        return TeacherResponse.builder()
                .teacherId(teacher.getTeacherId())
                .name(teacher.getName())
                .phone(teacher.getPhone())
                .email(teacher.getEmail())
                .build();
    }
    
    private StudentResponse convertToStudentResponse(Student student) {
        return StudentResponse.builder()
                .studentId(student.getStudentId())
                .name(student.getName())
                .phone(student.getPhone())
                .email(student.getEmail())
                .build();
    }
    
    private AdminResponse convertToAdminResponse(Admin admin) {
        return AdminResponse.builder()
                .adminId(admin.getAdminId())
                .name(admin.getName())
                .phone(admin.getPhone())
                .email(admin.getEmail())
                .build();
    }
} 